using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.ComponentModel.Design.Serialization;
using System.CodeDom;
using GuavaData;

namespace GuavaLib
{
    [DesignerSerializer(typeof(GLaunchControlSerializer<GSelect>),
       typeof(CodeDomSerializer))]
    public partial class GSelect : UserControl, Guava, GuavaLaunchBase
    {
        string gname;
        Type s;
        string gview;
        DataView dv;
        string strLaunchFile;
        List<string> columnsFound;
        string firstColumn;
        string gn;

        public delegate void ComboBoxItemChanged(object sender, GuavaRowSelectedEventArgs args);
        public event ComboBoxItemChanged SelectedItemChanged;
        
        public GSelect()
        {
            InitializeComponent();
            columnsFound = new List<string>();
            this.cmbItems.SelectedIndexChanged += new EventHandler(cmbItems_SelectedIndexChanged);
        }

        void cmbItems_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.SelectedItemChanged != null)
            {
                if (this.cmbItems.Items.Count > 0 && this.cmbItems.SelectedItem is GuavaData.Domain.AnnotatedGuid)
                {
                    SelectedItemChanged(this, new GuavaRowSelectedEventArgs(this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid));
                }
                else
                {
                    SelectedItemChanged(this, new GuavaRowSelectedEventArgs(null));
                }
            }
        }

        #region Guava Members

        [Browsable(false)]
        public LaunchType LType
        {
            get
            {
                return LaunchType.Multi;
            }
        }

        [Browsable(false)]
        public ControlType CType
        {
            get
            {
                return ControlType.None;
            }
        }

        public string GText
        {
            get
            {
                return this.lblTitle.Text;
            }

            set
            {
                this.lblTitle.Text = value;
            }
        }

        public string GName
        {
            get
            {
                if (gname == null) return this.Name;
                return gname;
            }
            set
            {
                gname = value;
            }
        }

        [Browsable(false)]
        public Point GLocation
        {
            get { return this.Location; }
        }

        [Browsable(false)]
        public Size GSize
        {
            get { return this.Size; }
        }

        [Browsable(false)]
        public List<Guava> GComponents
        {
            get
            {
                List<Guava> lg = new List<Guava>();
                if (s == null) return lg;

                ConstructorInfo ci = s.GetConstructor(new Type[] { });
                Object o = ci.Invoke(new object[] { });
                lg.Add(o as Guava);

                return lg;
            }
        }

        public void GBind(string table, int i)
        {
            // Find the name of the launched table
            ConstructorInfo ci = s.GetConstructor(new Type[] { });
            Object o = ci.Invoke(new object[] { });
            PropertyInfo p = s.GetProperty("GName");
            MethodInfo mi_name = p.GetGetMethod();
            gn = (mi_name.Invoke(o, null)) as string;

            // Retrieve the ID from the parent form
            Guid gid = ((this.ParentForm) as GuavaForm).ID;

            // Fetch the appropriate rows
            AppData.FetchRowsByFK(gn, gid);
            DataTable targetTable = AppData.ds.Tables[gn];

            // Accept loading of rows into memory
            targetTable.AcceptChanges();

            // Parse view attributes
            columnsFound.Clear();
            foreach (System.Data.DataColumn dc in targetTable.Columns)
            {
                string toFind = System.String.Concat("<", dc.ColumnName, ">");
                if (gview.Contains(toFind))
                {
                    columnsFound.Add(dc.ColumnName);
                    if (firstColumn == null) firstColumn = dc.ColumnName;
                    else if (gview.IndexOf(dc.ColumnName) < gview.IndexOf(firstColumn)) firstColumn = dc.ColumnName;
                }
            }

            // Create a view of the destination table that only has the current ID's
            if (!gid.Equals(new Guid())) dv = new DataView(targetTable, "fk='" + gid.ToString() + "'", firstColumn, DataViewRowState.CurrentRows);
            else dv = new DataView(AppData.ds.Tables[gn]);

            BuildList();
            this.cmbItems.Sorted = true;

            return;
        }

        [Browsable(false)]
        public string GDefault
        {
            get { return null; }
        }

        [Browsable(false)]
        public string GToolTip
        {
            get
            {
                Control p = this.ParentForm;
                if (!(p is Guava)) return null;

                // Once we find the parent, cast it as a GuavaForm
                // If there's no tooltip object, return null
                ToolTip t = (p as GuavaForm).GToolTipProvider;
                if (t == null) return null;

                // Fetch the tooltip and return it
                return t.GetToolTip(this);
            }
        }

        [Browsable(false)]
        public GuavaData.Domain.Domain GDomain
        {
            get
            {
                return null;
            }
        }

        public Guava this[string s]
        {
            get
            {
                foreach (Guava g in this.GComponents) if (g.GName == s) return g;
                return null;
            }
        }
        #endregion

        [Editor(typeof(GUIFilenameEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public string GLaunchType
        {
            get
            {
                return strLaunchFile;
            }

            set
            {
                strLaunchFile = value;
            }
        }
        
        public Type Launch
        {
            get
            {
                return s;
            }

            set
            {
                this.s = value;
            }
        }

        public string View
        {
            get
            {
                return gview;
            }
            set
            {
                gview = value;
            }
        }

        public Type GetLaunchType()
        {
            if ((null == s) && strLaunchFile.Length > 0)
            {
                s = GLaunchClass.RetrieveTypeFromFile(strLaunchFile, Assembly.GetCallingAssembly());
            }
            return s;
        }

        private void btnNew_Click(object sender, EventArgs e)
        {
            Guid gtemp = Guid.NewGuid();
            this.PrepareStackFrame();
            this.LaunchForm(gtemp, null, true);
            BuildList();
            SetObject(gtemp);
        }

        private void btnEdit_Click(object sender, EventArgs e)
        {
            if (this.cmbItems.Items.Count <= 0) return;
            if (this.cmbItems.SelectedIndex < 0) return;
            this.PrepareStackFrame();
            Guid gtemp = (this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid).Object;
            this.LaunchForm(gtemp, null, false);
            BuildList();
            SetObject(gtemp);
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (this.cmbItems.Items.Count <= 0) return;
            if (this.cmbItems.SelectedIndex < 0) return;
            Guid toDel = (this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid).Object;
            if (MessageBox.Show("Are you sure that you want to delete " + this.cmbItems.SelectedItem.ToString() + "?", "Confirm Delete", MessageBoxButtons.YesNo) == DialogResult.No) return;

            // If we get here, we've selected an item to delete and opted to delete it from memory and from DB
            AppData.DeleteRow(this.GComponents[0].GName, toDel);
            BuildList();
        }

        private void BuildList()
        {
            // Build the list of combobox items
            List<object> lo = new List<object>();
            foreach (System.Data.DataRowView dr in dv)
            {
                string temp = gview;
                foreach (string s in columnsFound)
                {
                    temp = temp.Replace(System.String.Concat("<", s, ">"), dr[System.String.Concat(s)].ToString());
                }
                lo.Add(new GuavaData.Domain.AnnotatedGuid((Guid)dr["id"], temp, dr));
            }

            // Restore objects and highlighted item
            GuavaData.Domain.AnnotatedGuid ag = this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid;
            this.cmbItems.Items.Clear();
            this.cmbItems.Items.AddRange(lo.ToArray());
            this.cmbItems.Refresh();
            if (ag != null) SetObject(ag.Object);
        }

        #region GuavaLaunchBase Members

        public DialogResult LaunchForm(Guid gid, string form, bool bNew)
        {
            DialogResult d;

            if (s == null) return DialogResult.Cancel;

            ConstructorInfo ci = s.GetConstructor(new Type[] { typeof(Guid), typeof(Guid), typeof(bool), typeof(GSeed) });
            GForm o = ci.Invoke(new object[] { gid, (this.ParentForm as GuavaForm).ID, bNew, (this.ParentForm as GuavaForm).GS }) as GForm;
            d = o.ShowDialog();

            // When done, refresh the items
            this.cmbItems.Refresh();
            return d;
        }

        public void PrepareStackFrame()
        {
            // Build the seed stack frame call list
            GuavaForm gForm = this.ParentForm as GuavaForm;
            GSeed gsTemp = gForm.GS;
            GSeedFrame gsfTemp = gsTemp.Peek();

            // Create a list of controls
            Control p = this;
            Control q = p.Parent;
            List<string> ls = new List<string>();
            ls.Add(this.GName);

            // Follow parents up the chain to the root form
            while (!(q is GuavaForm))
            {
                if (!(q is Guava))
                {
                    Guava r = q.Parent as Guava;
                    if (r == null) throw new Exception("Found two non-guava objects nested together in the same tree for marking.");
                    bool bfound = false;
                    foreach (Guava g in r.GComponents)
                    {
                        if (g.GComponents.Contains(p as Guava))
                        {
                            ls.Insert(0, g.GName);
                            bfound = true;
                            break;
                        }
                    }
                    if (!bfound) throw new Exception("Could not match a guava object for creating a seed for marking.");
                }
                else ls.Insert(0, (q as Guava).GName);
                p = p.Parent;
                q = q.Parent;
            }
            gsfTemp.controls = ls;
        }

        public void SetObject(Guid id)
        {
            foreach (object o in this.cmbItems.Items)
            {
                GuavaData.Domain.AnnotatedGuid ag = o as GuavaData.Domain.AnnotatedGuid;
                if (ag.Object.Equals(id))
                {
                    this.cmbItems.SelectedItem = o;
                    return;
                }
            }
            
            this.cmbItems.SelectedIndex = -1;
            this.cmbItems.Refresh();
        }

        #endregion
    }
    
    public class GuavaRowSelectedEventArgs : EventArgs
    {
        public GuavaData.Domain.AnnotatedGuid RowInfo;

        public GuavaRowSelectedEventArgs(GuavaData.Domain.AnnotatedGuid ag)
            : base()
        {
            this.RowInfo = ag;
        }
    }
    
    public class GLaunchControlSerializer<T> : CodeDomSerializer
    {
        public override object Serialize(
        IDesignerSerializationManager manager,
        object value)
        {
            CodeDomSerializer baseSerializer =
            (CodeDomSerializer)manager.GetSerializer(
            typeof(T).BaseType,
            typeof(CodeDomSerializer));

            object codeObject = baseSerializer.Serialize(manager, value);

            if (codeObject is CodeStatementCollection)
            {
                CodeStatementCollection statements =
                    (CodeStatementCollection)codeObject;

                // call a custom method.
                //
                CodeExpression targetObject =
                   base.SerializeToExpression(manager, value);
                if (targetObject != null)
                {
                    CodeMethodInvokeExpression methodCall =
                        new CodeMethodInvokeExpression(targetObject, "GetLaunchType");

                    CodePropertyReferenceExpression variableRef = new
                                    CodePropertyReferenceExpression(targetObject, "Launch");

                    CodeAssignStatement assign = new CodeAssignStatement();
                    assign.Left = variableRef;
                    assign.Right = methodCall;

                    statements.Add(assign);
                }
            }
            // finally, return the statements that have been created
            return codeObject;
        }
    }
}
